home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _B80B4A61C1B74531A3E447DF493CC77E < prev    next >
Encoding:
Text File  |  2002-06-30  |  45.5 KB  |  2,042 lines

  1. // Copyright (C) 2001-2002 Raven Software.
  2. //
  3. #include "g_local.h"
  4.  
  5. #include "../../ui/menudef.h"
  6.  
  7. int AcceptBotCommand(char *cmd, gentity_t *pl);
  8.  
  9. /*
  10. ==================
  11. DeathmatchScoreboardMessage
  12. ==================
  13. */
  14. void DeathmatchScoreboardMessage( gentity_t *ent ) 
  15. {
  16.     char        entry[1024];
  17.     char        string[1400];
  18.     int            stringlength;
  19.     int            i, j;
  20.     gclient_t    *cl;
  21.     int            numSorted;
  22.  
  23.     // send the latest information on all clients
  24.     string[0]    = 0;
  25.     stringlength = 0;
  26.  
  27.     numSorted = level.numConnectedClients;
  28.     
  29.     for (i=0 ; i < numSorted ; i++) 
  30.     {
  31.         int    ping;
  32.  
  33.         cl = &level.clients[level.sortedClients[i]];
  34.  
  35.         if ( cl->pers.connected == CON_CONNECTING ) 
  36.         {
  37.             ping = -1;
  38.         } 
  39.         else 
  40.         {
  41.             ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
  42.         }
  43.     
  44.         Com_sprintf (entry, sizeof(entry),
  45.             " %i %i %i %i %i %i %i %i %i", 
  46.             level.sortedClients[i],
  47.             cl->sess.score, 
  48.             cl->sess.kills, 
  49.             cl->sess.deaths, 
  50.             ping, 
  51.             (level.time - cl->pers.enterTime)/60000,
  52.             (cl->sess.ghost || cl->ps.pm_type == PM_DEAD) ? qtrue : qfalse,
  53.             g_entities[level.sortedClients[i]].s.gametypeitems,
  54.             g_teamkillDamageMax.integer ? 100 * cl->sess.teamkillDamage / g_teamkillDamageMax.integer : 0
  55.             );
  56.  
  57.         j = strlen(entry);
  58.         if (stringlength + j > 1022 )
  59.         {
  60.             break;
  61.         }
  62.  
  63.         strcpy (string + stringlength, entry);
  64.         stringlength += j;
  65.     }
  66.  
  67.     trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i, 
  68.                             level.teamScores[TEAM_RED], 
  69.                             level.teamScores[TEAM_BLUE],
  70.                             string ) );
  71. }
  72.  
  73.  
  74. /*
  75. ==================
  76. Cmd_Score_f
  77.  
  78. Request current scoreboard information
  79. ==================
  80. */
  81. void Cmd_Score_f( gentity_t *ent ) 
  82. {
  83.     DeathmatchScoreboardMessage( ent );
  84. }
  85.  
  86. /*
  87. ==================
  88. CheatsOk
  89. ==================
  90. */
  91. qboolean    CheatsOk( gentity_t *ent ) {
  92.     if ( !g_cheats.integer ) {
  93.         trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
  94.         return qfalse;
  95.     }
  96.     if ( ent->health <= 0 ) {
  97.         trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\""));
  98.         return qfalse;
  99.     }
  100.     return qtrue;
  101. }
  102.  
  103.  
  104. /*
  105. ==================
  106. ConcatArgs
  107. ==================
  108. */
  109. char    *ConcatArgs( int start ) {
  110.     int        i, c, tlen;
  111.     static char    line[MAX_STRING_CHARS];
  112.     int        len;
  113.     char    arg[MAX_STRING_CHARS];
  114.  
  115.     len = 0;
  116.     c = trap_Argc();
  117.     for ( i = start ; i < c ; i++ ) {
  118.         trap_Argv( i, arg, sizeof( arg ) );
  119.         tlen = strlen( arg );
  120.         if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
  121.             break;
  122.         }
  123.         memcpy( line + len, arg, tlen );
  124.         len += tlen;
  125.         if ( i != c - 1 ) {
  126.             line[len] = ' ';
  127.             len++;
  128.         }
  129.     }
  130.  
  131.     line[len] = 0;
  132.  
  133.     return line;
  134. }
  135.  
  136. /*
  137. ==================
  138. SanitizeString
  139.  
  140. Remove case and control characters
  141. ==================
  142. */
  143. void SanitizeString( char *in, char *out ) {
  144.     while ( *in ) {
  145.         if ( *in == 27 ) {
  146.             in += 2;        // skip color code
  147.             continue;
  148.         }
  149.         if ( *in < 32 ) {
  150.             in++;
  151.             continue;
  152.         }
  153.         *out++ = tolower( *in++ );
  154.     }
  155.  
  156.     *out = 0;
  157. }
  158.  
  159. /*
  160. ==================
  161. G_ClientNumberFromName
  162.  
  163. Finds the client number of the client with the given name
  164. ==================
  165. */
  166. int G_ClientNumberFromName ( const char* name )
  167. {
  168.     char        s2[MAX_STRING_CHARS];
  169.     char        n2[MAX_STRING_CHARS];
  170.     int            i;
  171.     gclient_t*    cl;
  172.  
  173.     // check for a name match
  174.     SanitizeString( (char*)name, s2 );
  175.     for ( i=0, cl=level.clients ; i < level.numConnectedClients ; i++, cl++ ) 
  176.     {
  177.         SanitizeString( cl->pers.netname, n2 );
  178.         if ( !strcmp( n2, s2 ) ) 
  179.         {
  180.             return i;
  181.         }
  182.     }
  183.  
  184.     return -1;
  185. }
  186.  
  187. /*
  188. ==================
  189. ClientNumberFromString
  190.  
  191. Returns a player number for either a number or name string
  192. Returns -1 if invalid
  193. ==================
  194. */
  195. int ClientNumberFromString( gentity_t *to, char *s ) {
  196.     gclient_t    *cl;
  197.     int            idnum;
  198.     char        s2[MAX_STRING_CHARS];
  199.     char        n2[MAX_STRING_CHARS];
  200.  
  201.     // numeric values are just slot numbers
  202.     if (s[0] >= '0' && s[0] <= '9') {
  203.         idnum = atoi( s );
  204.         if ( idnum < 0 || idnum >= level.maxclients ) {
  205.             trap_SendServerCommand( to-g_entities, va("print \"Bad client slot: %i\n\"", idnum));
  206.             return -1;
  207.         }
  208.  
  209.         cl = &level.clients[idnum];
  210.         if ( cl->pers.connected != CON_CONNECTED ) {
  211.             trap_SendServerCommand( to-g_entities, va("print \"Client %i is not active\n\"", idnum));
  212.             return -1;
  213.         }
  214.         return idnum;
  215.     }
  216.  
  217.     // check for a name match
  218.     SanitizeString( s, s2 );
  219.     for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) {
  220.         if ( cl->pers.connected != CON_CONNECTED ) {
  221.             continue;
  222.         }
  223.         SanitizeString( cl->pers.netname, n2 );
  224.         if ( !strcmp( n2, s2 ) ) {
  225.             return idnum;
  226.         }
  227.     }
  228.  
  229.     trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s));
  230.     return -1;
  231. }
  232.  
  233. /*
  234. ==================
  235. Cmd_Drop_f
  236.  
  237. Drops the currenty selected weapon
  238. ==================
  239. */
  240. void Cmd_Drop_f ( gentity_t* ent )
  241. {
  242.     gentity_t* dropped;
  243.  
  244.     // spectators cant drop anything since they dont have anything
  245.     if ( ent->client->sess.team == TEAM_SPECTATOR )
  246.     {
  247.         return;
  248.     }
  249.  
  250.     // Ghosts and followers cant drop stuff
  251.     if ( ent->client->ps.pm_flags & (PMF_GHOST|PMF_FOLLOW) )
  252.     {
  253.         return;
  254.     }
  255.  
  256.     // Drop the weapon the client wanted to drop
  257.     dropped = G_DropWeapon ( ent, atoi(ConcatArgs( 1 )), 3000 );
  258.     if ( !dropped )
  259.     {
  260.         return;
  261.     }
  262. }
  263.  
  264. /*
  265. ==================
  266. Cmd_DropItem_f
  267.  
  268. Drops the gametype items the player is carrying
  269. ==================
  270. */
  271. void Cmd_DropItem_f ( gentity_t* ent )
  272. {
  273.     // spectators cant drop anything since they dont have anything
  274.     if ( ent->client->sess.team == TEAM_SPECTATOR )
  275.     {
  276.         return;
  277.     }
  278.  
  279.     // Ghosts and followers cant drop stuff
  280.     if ( ent->client->ps.pm_flags & (PMF_GHOST|PMF_FOLLOW) )
  281.     {
  282.         return;
  283.     }
  284.  
  285.     // Nothing to drop
  286.     if ( !ent->client->ps.stats[STAT_GAMETYPE_ITEMS] )
  287.     {
  288.         return;
  289.     }
  290.  
  291.     G_DropGametypeItems ( ent );
  292. }
  293.  
  294. /*
  295. ==================
  296. Cmd_Give_f
  297.  
  298. Give items to a client
  299. ==================
  300. */
  301. void Cmd_Give_f (gentity_t *ent)
  302. {
  303.     char        *name;
  304.     gitem_t        *it;
  305.     int            i;
  306.     qboolean    give_all;
  307.     gentity_t        *it_ent;
  308.     trace_t        trace;
  309.     char        arg[MAX_QPATH];
  310.  
  311.     int start;
  312.     int end;
  313.     int l;
  314.  
  315.     trap_Argv( 1, arg, sizeof( arg ) );
  316.  
  317.     if ( !Q_stricmp ( arg, "me" ) )
  318.     {
  319.         start = ent->s.number;
  320.         end = start + 1;
  321.     }
  322.     else if ( !Q_stricmp ( arg, "all" ) )
  323.     {
  324.         start = 0;
  325.         end   = MAX_CLIENTS;
  326.     }
  327.     else
  328.     {
  329.         start = atoi ( arg );
  330.         end = start + 1;
  331.     }
  332.  
  333.     for ( l = start; l < end; l ++ )
  334.     {
  335.         ent = &g_entities[l];
  336.  
  337.         if ( !ent->inuse )
  338.         {
  339.             continue;
  340.         }
  341.  
  342.         if ( G_IsClientDead ( ent->client ) )
  343.         {
  344.             continue;
  345.         }
  346.  
  347.         if ( !CheatsOk( ent ) ) {
  348.             return;
  349.         }
  350.  
  351.     name = ConcatArgs( 2 );
  352.  
  353.     if (Q_stricmp(name, "all") == 0)
  354.         give_all = qtrue;
  355.     else
  356.         give_all = qfalse;
  357.  
  358.     if (give_all || Q_stricmp( name, "health") == 0)
  359.     {
  360.         ent->health = MAX_HEALTH;
  361.         if (!give_all)
  362.             continue;
  363.     }
  364.  
  365.     if (give_all || Q_stricmp(name, "weapons") == 0)
  366.     {
  367.         ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - ( 1 << WP_NONE );
  368.         if (!give_all)
  369.             continue;
  370.     }
  371.  
  372.     if (give_all || Q_stricmp(name, "ammo") == 0)
  373.     {
  374.         for ( i = WP_NONE + 1 ; i < WP_NUM_WEAPONS ; i++ ) 
  375.         {
  376.             attackType_t a;
  377.     
  378.             for ( a = ATTACK_NORMAL; a < ATTACK_MAX; a ++ )
  379.             {
  380.                 ent->client->ps.clip[a][i] = weaponData[i].attack[a].clipSize;
  381.                 ent->client->ps.ammo[weaponData[i].attack[a].ammoIndex] = ammoData[weaponData[i].attack[a].ammoIndex].max;
  382.             }
  383.         }
  384.  
  385.         if (!give_all)
  386.             continue;
  387.     }
  388.  
  389.     if (give_all || Q_stricmp(name, "armor") == 0)
  390.     {
  391.         ent->client->ps.stats[STAT_ARMOR] = MAX_ARMOR;
  392.  
  393.         if (!give_all)
  394.             continue;
  395.     }
  396.  
  397.     // spawn a specific item right on the player
  398.     if ( !give_all ) 
  399.     {
  400.         it = BG_FindItem (name);
  401.         if (!it) 
  402.         {
  403.             continue;
  404.         }
  405.  
  406.         if ( it->giType == IT_GAMETYPE )
  407.         {
  408.             continue;
  409.         }
  410.  
  411.         it_ent = G_Spawn();
  412.         VectorCopy( ent->r.currentOrigin, it_ent->s.origin );
  413.         it_ent->classname = it->classname;
  414.         G_SpawnItem (it_ent, it);
  415.         FinishSpawningItem(it_ent );
  416.         memset( &trace, 0, sizeof( trace ) );
  417.         Touch_Item (it_ent, ent, &trace);
  418.         if (it_ent->inuse) 
  419.         {
  420.             G_FreeEntity( it_ent );
  421.         }
  422.     }
  423.  
  424.     }
  425. }
  426.  
  427.  
  428. /*
  429. ==================
  430. Cmd_God_f
  431.  
  432. Sets client to godmode
  433.  
  434. argv(0) god
  435. ==================
  436. */
  437. void Cmd_God_f (gentity_t *ent)
  438. {
  439.     char    *msg;
  440.  
  441.     if ( !CheatsOk( ent ) ) {
  442.         return;
  443.     }
  444.  
  445.     ent->flags ^= FL_GODMODE;
  446.     if (!(ent->flags & FL_GODMODE) )
  447.         msg = "godmode OFF\n";
  448.     else
  449.         msg = "godmode ON\n";
  450.  
  451.     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
  452. }
  453.  
  454.  
  455. /*
  456. ==================
  457. Cmd_Notarget_f
  458.  
  459. Sets client to notarget
  460.  
  461. argv(0) notarget
  462. ==================
  463. */
  464. void Cmd_Notarget_f( gentity_t *ent ) {
  465.     char    *msg;
  466.  
  467.     if ( !CheatsOk( ent ) ) {
  468.         return;
  469.     }
  470.  
  471.     ent->flags ^= FL_NOTARGET;
  472.     if (!(ent->flags & FL_NOTARGET) )
  473.         msg = "notarget OFF\n";
  474.     else
  475.         msg = "notarget ON\n";
  476.  
  477.     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
  478. }
  479.  
  480.  
  481. /*
  482. ==================
  483. Cmd_Noclip_f
  484.  
  485. argv(0) noclip
  486. ==================
  487. */
  488. void Cmd_Noclip_f( gentity_t *ent ) {
  489.     char    *msg;
  490.  
  491.     if ( !CheatsOk( ent ) ) {
  492.         return;
  493.     }
  494.  
  495.     if ( ent->client->noclip ) {
  496.         msg = "noclip OFF\n";
  497.     } else {
  498.         msg = "noclip ON\n";
  499.     }
  500.     ent->client->noclip = !ent->client->noclip;
  501.  
  502.     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
  503. }
  504.  
  505.  
  506. /*
  507. ==================
  508. Cmd_LevelShot_f
  509.  
  510. This is just to help generate the level pictures
  511. for the menus.  It goes to the intermission immediately
  512. and sends over a command to the client to resize the view,
  513. hide the scoreboard, and take a special screenshot
  514. ==================
  515. */
  516. void Cmd_LevelShot_f( gentity_t *ent ) 
  517. {
  518.     if ( !CheatsOk( ent ) ) 
  519.     {
  520.         return;
  521.     }
  522.  
  523.     BeginIntermission();
  524.  
  525.     trap_SendServerCommand( ent-g_entities, "clientLevelShot" );
  526. }
  527.  
  528. /*
  529. =================
  530. Cmd_Kill_f
  531. =================
  532. */
  533. void Cmd_Kill_f( gentity_t *ent ) 
  534. {
  535.     // No killing yourself if your a spectator
  536.     if ( G_IsClientSpectating ( ent->client ) )
  537.     {
  538.         return;
  539.     }
  540.  
  541.     // No killing yourself if your dead
  542.     if ( G_IsClientDead ( ent->client ) ) 
  543.     {
  544.         return;
  545.     }
  546.  
  547.     ent->flags &= ~FL_GODMODE;
  548.     ent->client->ps.stats[STAT_HEALTH] = ent->health = -999;
  549.     player_die (ent, ent, ent, 100000, MOD_SUICIDE, HL_NONE, vec3_origin );
  550. }
  551.  
  552. /*
  553. =================
  554. BroadCastTeamChange
  555.  
  556. Let everyone know about a team change
  557. =================
  558. */
  559. void BroadcastTeamChange( gclient_t *client, int oldTeam )
  560. {
  561.     switch ( client->sess.team )
  562.     {
  563.         case TEAM_RED:
  564.             trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"", client->pers.netname) );
  565.             break;
  566.  
  567.         case TEAM_BLUE:
  568.             trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"", client->pers.netname));
  569.             break;
  570.  
  571.         case TEAM_SPECTATOR:
  572.             if ( oldTeam != TEAM_SPECTATOR )
  573.             {
  574.                 trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"", client->pers.netname));
  575.             }
  576.             break;
  577.  
  578.         case TEAM_FREE:
  579.             trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"", client->pers.netname));
  580.             break;
  581.     }
  582. }
  583.  
  584. /*
  585. =================
  586. SetTeam
  587. =================
  588. */
  589. void SetTeam( gentity_t *ent, char *s, const char* identity ) 
  590. {
  591.     int                    team;
  592.     int                    oldTeam;
  593.     gclient_t            *client;
  594.     int                    clientNum;
  595.     spectatorState_t    specState;
  596.     int                    specClient;
  597.     qboolean            ghost;
  598.        qboolean            noOutfittingChange = qfalse;
  599.  
  600.     // see what change is requested
  601.     //
  602.     client = ent->client;
  603.  
  604.     clientNum = client - level.clients;
  605.     specClient = 0;
  606.     specState = SPECTATOR_NOT;
  607.  
  608.     // If an identity was specified then inject it into
  609.     // the clients userinfo
  610.     if ( identity )
  611.     {
  612.         char userinfo[MAX_INFO_STRING];
  613.         trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
  614.  
  615.         if ( Q_stricmp ( identity, Info_ValueForKey ( userinfo, "identity" ) ) )
  616.         {
  617.             Info_SetValueForKey ( userinfo, "identity", identity );
  618.             Info_SetValueForKey ( userinfo, "team_identity", identity );
  619.             trap_SetUserinfo ( clientNum, userinfo );
  620.         }
  621.         else
  622.         {
  623.             identity = NULL;
  624.         }
  625.     }
  626.  
  627.     if ( !Q_stricmp( s, "follow1" ) ) 
  628.     {
  629.         team = TEAM_SPECTATOR;
  630.         specState = SPECTATOR_FOLLOW;
  631.         specClient = -1;
  632.     } 
  633.     else if ( !Q_stricmp( s, "follow2" ) ) 
  634.     {
  635.         team = TEAM_SPECTATOR;
  636.         specState = SPECTATOR_FOLLOW;
  637.         specClient = -2;
  638.     } 
  639.     else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) 
  640.     {
  641.         team = TEAM_SPECTATOR;
  642.         specState = SPECTATOR_FREE;
  643.     } 
  644.     else if ( level.gametypeData->teams ) 
  645.     {
  646.         // if running a team game, assign player to one of the teams
  647.         specState = SPECTATOR_NOT;
  648.         if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) 
  649.         {
  650.             team = TEAM_RED;
  651.         } 
  652.         else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) 
  653.         {
  654.             team = TEAM_BLUE;
  655.         } 
  656.         else 
  657.         {
  658.             // pick the team with the least number of players
  659.             team = PickTeam( clientNum );
  660.         }
  661.  
  662.         if ( g_teamForceBalance.integer  ) 
  663.         {
  664.             int        counts[TEAM_NUM_TEAMS];
  665.  
  666.             counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE, NULL );
  667.             counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED, NULL );
  668.  
  669.             // We allow a spread of two
  670.             if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) 
  671.             {
  672.                 trap_SendServerCommand( ent->client->ps.clientNum, 
  673.                                         "cp \"Red team has too many players.\n\"" );
  674.  
  675.                 // ignore the request
  676.                 return; 
  677.             }
  678.             if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) 
  679.             {
  680.                 trap_SendServerCommand( ent->client->ps.clientNum, 
  681.                                         "cp \"Blue team has too many players.\n\"" );
  682.  
  683.                 // ignore the request
  684.                 return; 
  685.             }
  686.  
  687.             // It's ok, the team we are switching to has less or same number of players
  688.         }
  689.     } 
  690.     else 
  691.     {
  692.         // force them to spectators if there aren't any spots free
  693.         team = TEAM_FREE;
  694.     }
  695.  
  696.     // override decision if limiting the players
  697.     if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) 
  698.     {
  699.         team = TEAM_SPECTATOR;
  700.     }
  701.  
  702.     // decide if we will allow the change
  703.     oldTeam = client->sess.team;
  704.     ghost   = client->sess.ghost;
  705.  
  706.     if ( team == oldTeam && team != TEAM_SPECTATOR )
  707.     {
  708.         if ( identity )
  709.         {
  710.             // get and distribute relevent paramters
  711.             client->pers.identity = NULL;
  712.             ClientUserinfoChanged( clientNum );
  713.         }
  714.         
  715.         return;
  716.     }
  717.  
  718.        noOutfittingChange = ent->client->noOutfittingChange;
  719.  
  720.     // he starts at 'base'
  721.     client->pers.teamState.state = TEAM_BEGIN;
  722.     
  723.     if ( oldTeam != TEAM_SPECTATOR ) 
  724.     {
  725.         if ( ghost )
  726.         {
  727.             G_StopGhosting ( ent );
  728.         }
  729.         else if ( !G_IsClientDead ( client ) )
  730.         {
  731.             // Kill him (makes sure he loses flags, etc)
  732.             ent->flags &= ~FL_GODMODE;
  733.             ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
  734.             player_die (ent, ent, ent, 100000, MOD_TEAMCHANGE, HL_NONE, vec3_origin );
  735.  
  736.             ent->client->sess.ghost = qfalse;
  737.         }
  738.     }
  739.  
  740.     // If respawn interval start as a ghost
  741.     if ( level.gametypeRespawnTime[ team ] )
  742.     {
  743.         ghost = qtrue;
  744.     }
  745.  
  746.     // they go to the end of the line
  747.     if ( team == TEAM_SPECTATOR ) 
  748.     {
  749.         client->sess.spectatorTime = level.time;
  750.     }
  751.  
  752.     client->sess.team = team;
  753.     client->sess.spectatorState = specState;
  754.     client->sess.spectatorClient = specClient;
  755.  
  756.     // Always spawn into a ctf game using a respawn timer.
  757.     if ( team != TEAM_SPECTATOR && level.gametypeData->respawnType == RT_INTERVAL )
  758.     {
  759.         G_SetRespawnTimer ( ent );
  760.         ghost = qtrue;
  761.     }
  762.  
  763.     BroadcastTeamChange( client, oldTeam );
  764.  
  765.     // See if we should spawn as a ghost
  766.     if ( team != TEAM_SPECTATOR && level.gametypeData->respawnType == RT_NONE )
  767.     {
  768.         // If there are ghosts already then spawn as a ghost because
  769.         // the game is already in progress.
  770.         if ( (level.gametypeJoinTime && (level.time - level.gametypeJoinTime) > (g_roundjointime.integer * 1000)) || noOutfittingChange || client->sess.noTeamChange )
  771.         {
  772.             ghost = qtrue;
  773.         }
  774.  
  775.         // Spectator to a team doesnt count
  776.         if ( oldTeam != TEAM_SPECTATOR )
  777.         {
  778.             client->sess.noTeamChange = qtrue;
  779.         }
  780.     }
  781.  
  782.     // If a ghost, enforce it
  783.     if ( ghost )
  784.     {
  785.         // Make them a ghost again
  786.         if ( team != TEAM_SPECTATOR )
  787.         {
  788.             G_StartGhosting ( ent );
  789.  
  790.             // get and distribute relevent paramters
  791.             client->pers.identity = NULL;
  792.             ClientUserinfoChanged( clientNum );
  793.  
  794.             CalculateRanks();
  795.  
  796.             return;
  797.         }
  798.     }    
  799.  
  800.     // get and distribute relevent paramters
  801.     client->pers.identity = NULL;
  802.     ClientUserinfoChanged( clientNum );
  803.  
  804.     CalculateRanks();
  805.  
  806.     // Begin the clients new life on the their new team
  807.     ClientBegin( clientNum );
  808. }
  809.  
  810. /*
  811. =================
  812. G_StartGhosting
  813.  
  814. Starts a client ghosting.  This essentially will kill a player which is alive
  815. =================
  816. */
  817. void G_StartGhosting ( gentity_t* ent )
  818. {
  819.     int i;
  820.  
  821.     // Dont start ghosting if already ghosting
  822.     if ( ent->client->sess.ghost )
  823.     {
  824.         return;
  825.     }
  826.  
  827.     ent->client->sess.ghost = qtrue;
  828.     ent->client->sess.spectatorState = SPECTATOR_FREE;
  829.     ent->client->sess.spectatorClient = -1;
  830.     ent->client->ps.pm_flags |= PMF_GHOST;
  831.     ent->client->ps.stats[STAT_HEALTH] = 100;
  832.     ent->client->ps.pm_type = PM_SPECTATOR;
  833.     ent->client->ps.pm_flags &= ~PMF_FOLLOW;
  834.  
  835.     trap_UnlinkEntity (ent);
  836.  
  837.     // stop any following clients
  838.     for ( i = 0 ; i < level.maxclients ; i++ ) 
  839.     {
  840.         if ( G_IsClientSpectating ( &level.clients[i] )
  841.             && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW
  842.             && level.clients[i].sess.spectatorClient == ent->s.number ) 
  843.         {
  844.             G_StopFollowing( &g_entities[i] );
  845.         }
  846.     }
  847. }
  848.  
  849. /*
  850. =================
  851. G_StopGhosting
  852.  
  853. Stops a client from ghosting.  The client will be dead after this
  854. call
  855. =================
  856. */
  857. void G_StopGhosting ( gentity_t* ent )
  858. {
  859.     // Dont stop someone who isnt ghosting in the first place
  860.     if ( !ent->client->sess.ghost )
  861.     {
  862.         return;
  863.     }
  864.  
  865.     ent->client->sess.ghost = qfalse;
  866.     ent->client->ps.pm_flags &= ~PMF_GHOST;
  867.     ent->client->ps.pm_flags &= ~PMF_FOLLOW;
  868.  
  869.     if ( ent->client->sess.team == TEAM_SPECTATOR )
  870.     {
  871.         ent->client->ps.pm_type = PM_SPECTATOR;
  872.     }
  873.     else
  874.     {
  875.         ent->client->ps.pm_type = PM_DEAD;
  876.         ent->health = ent->client->ps.stats[STAT_HEALTH] = 0;
  877.     }
  878. }
  879.  
  880. /*
  881. =================
  882. G_StopFollowing
  883.  
  884. If the client being followed leaves the game, or you just want to drop
  885. to free floating spectator mode
  886. =================
  887. */
  888. void G_StopFollowing( gentity_t *ent ) 
  889. {
  890.     // Cant stop following if not following in the first place
  891.     if ( !(ent->client->ps.pm_flags&PMF_FOLLOW) )
  892.     {
  893.         return;
  894.     }
  895.  
  896.     // Clear the following variables
  897.     ent->client->ps.pm_flags &= ~PMF_FOLLOW;
  898.     ent->client->sess.spectatorState = SPECTATOR_FREE;
  899.     ent->client->ps.clientNum = ent - g_entities;
  900.     ent->client->ps.zoomFov = 0;
  901.     ent->client->ps.loopSound = 0;
  902.     ent->client->ps.pm_flags &= ~(PMF_GOGGLES_ON|PMF_ZOOM_FLAGS);
  903.     ent->client->ps.persistant[PERS_TEAM] = ent->client->sess.team;
  904.     ent->r.svFlags &= ~SVF_BOT;
  905.  
  906.     // Ghots dont really become spectators, just psuedo spectators
  907.     if ( ent->client->sess.ghost )
  908.     {
  909.         // Do a start and stop to ensure the variables are all set properly
  910.         G_StopGhosting ( ent );
  911.         G_StartGhosting ( ent );
  912.     }
  913.     else
  914.     {
  915.         ent->client->sess.team = TEAM_SPECTATOR;    
  916.         ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;    
  917.     }
  918.  
  919.     // If we were in fact following someone, then make the angles and origin nice for
  920.     // when we stop
  921.        if ( ent->client->sess.spectatorClient != -1 )
  922.        {
  923.            gclient_t* cl = &level.clients[ent->client->sess.spectatorClient];
  924.     
  925.            int i;
  926.            for ( i = 0; i < 3; i ++ )
  927.         {
  928.                ent->client->ps.delta_angles[i] = ANGLE2SHORT(cl->ps.viewangles[i] - SHORT2ANGLE(ent->client->pers.cmd.angles[i]));
  929.         }
  930.     
  931.            VectorCopy ( cl->ps.viewangles, ent->client->ps.viewangles );
  932.            VectorCopy ( cl->ps.origin, ent->client->ps.origin );
  933.            VectorClear ( ent->client->ps.velocity );
  934.            ent->client->ps.movementDir = 0;
  935.     
  936.            BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
  937.        }        
  938.  
  939.     ent->client->sess.spectatorClient = -1;
  940. }
  941.  
  942. /*
  943. =================
  944. Cmd_Team_f
  945. =================
  946. */
  947. void Cmd_Team_f( gentity_t *ent ) 
  948. {
  949.     char team[MAX_TOKEN_CHARS];
  950.     char identity[MAX_TOKEN_CHARS];
  951.  
  952.     // Need at least the team specified in the arguments
  953.     if ( trap_Argc() < 2 ) 
  954.     {
  955.         int oldTeam = ent->client->sess.team;
  956.         switch ( oldTeam ) 
  957.         {
  958.             case TEAM_BLUE:
  959.                 trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" );
  960.                 break;
  961.             
  962.             case TEAM_RED:
  963.                 trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" );
  964.                 break;
  965.             
  966.             case TEAM_FREE:
  967.                 trap_SendServerCommand( ent-g_entities, "print \"Free team\n\"" );
  968.                 break;
  969.         
  970.             case TEAM_SPECTATOR:
  971.                 trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" );
  972.                 break;
  973.         }
  974.         
  975.         return;
  976.     }
  977.  
  978.     // Limit how often one can switch team
  979.     if ( ent->client->switchTeamTime > level.time ) 
  980.     {
  981.         trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" );
  982.         return;
  983.     }
  984.  
  985.     trap_Argv( 1, team, sizeof( team ) );
  986.     trap_Argv( 2, identity, sizeof( identity ) );
  987.  
  988.     SetTeam( ent, team, identity[0]?identity:NULL );
  989.  
  990.     // Remember the team switch time so they cant do it again really quick
  991.     ent->client->switchTeamTime = level.time + 5000;
  992. }
  993.  
  994.  
  995. /*
  996. =================
  997. Cmd_Follow_f
  998. =================
  999. */
  1000. void Cmd_Follow_f( gentity_t *ent ) 
  1001. {
  1002.     int        i;
  1003.     char    arg[MAX_TOKEN_CHARS];
  1004.  
  1005.     if ( trap_Argc() != 2 ) 
  1006.     {
  1007.         if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) 
  1008.         {
  1009.             G_StopFollowing( ent );
  1010.         }
  1011.         return;
  1012.     }
  1013.  
  1014.     trap_Argv( 1, arg, sizeof( arg ) );
  1015.     i = ClientNumberFromString( ent, arg );
  1016.     if ( i == -1 ) 
  1017.     {
  1018.         return;
  1019.     }
  1020.  
  1021.     // can't follow self
  1022.     if ( &level.clients[ i ] == ent->client ) 
  1023.     {
  1024.         return;
  1025.     }
  1026.  
  1027.     // cant cycle to dead people
  1028.     if ( level.clients[i].ps.pm_type == PM_DEAD )
  1029.     {
  1030.         return;
  1031.     }
  1032.  
  1033.     // can't follow another spectator
  1034.     if ( G_IsClientSpectating ( &level.clients[ i ] ) )
  1035.     {
  1036.         return;
  1037.     }
  1038.  
  1039.     // Dissallow following of the enemy if the cvar is set
  1040.     if ( level.gametypeData->teams && !g_followEnemy.integer && ent->client->sess.team != TEAM_SPECTATOR )
  1041.     {
  1042.         // Are they on the same team?
  1043.         if ( level.clients[ i ].sess.team != ent->client->sess.team )
  1044.         {
  1045.             return;
  1046.         }
  1047.     }
  1048.  
  1049.     // first set them to spectator as long as they arent a ghost
  1050.     if ( !ent->client->sess.ghost && ent->client->sess.team != TEAM_SPECTATOR ) 
  1051.     {
  1052.         SetTeam( ent, "spectator", NULL );
  1053.     }
  1054.  
  1055.     ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
  1056.     ent->client->sess.spectatorClient = i;
  1057. }
  1058.  
  1059. /*
  1060. =================
  1061. Cmd_FollowCycle_f
  1062. =================
  1063. */
  1064. void Cmd_FollowCycle_f( gentity_t *ent, int dir ) 
  1065. {
  1066.     int        clientnum;
  1067.     int        deadclient;
  1068.     int        original;
  1069.  
  1070.     // first set them to spectator
  1071.     if ( !ent->client->sess.ghost && ent->client->sess.team != TEAM_SPECTATOR ) 
  1072.     {
  1073.         SetTeam( ent, "spectator", NULL );
  1074.     }
  1075.  
  1076.     if ( dir != 1 && dir != -1 ) 
  1077.     {
  1078.         Com_Error( ERR_FATAL, "Cmd_FollowCycle_f: bad dir %i", dir );
  1079.     }
  1080.  
  1081.     if ( ent->client->sess.spectatorClient == -1 )
  1082.     {
  1083.         clientnum = original = ent->s.number;
  1084.     }
  1085.     else
  1086.     {
  1087.         clientnum = original = ent->client->sess.spectatorClient;
  1088.     }
  1089.  
  1090.     deadclient = -1;
  1091.     do 
  1092.     {
  1093.         clientnum += dir;
  1094.         if ( clientnum >= level.maxclients ) 
  1095.         {
  1096.             clientnum = 0;
  1097.         }
  1098.         if ( clientnum < 0 ) 
  1099.         {
  1100.             clientnum = level.maxclients - 1;
  1101.         }
  1102.  
  1103.         // can only follow connected clients
  1104.         if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) 
  1105.         {
  1106.             continue;
  1107.         }
  1108.  
  1109.         // can't follow another spectator
  1110.         if ( G_IsClientSpectating ( &level.clients[ clientnum ] ) ) 
  1111.         {
  1112.             continue;
  1113.         }
  1114.  
  1115.         // Cant switch to dead people unless there is nobody else to switch to
  1116.         if ( G_IsClientDead ( &level.clients[clientnum] ) )
  1117.         {        
  1118.             deadclient = clientnum;
  1119.             continue;
  1120.         }
  1121.  
  1122.         // Dissallow following of the enemy if the cvar is set
  1123.         if ( level.gametypeData->teams && !g_followEnemy.integer && ent->client->sess.team != TEAM_SPECTATOR )
  1124.         {
  1125.             // Are they on the same team?
  1126.             if ( level.clients[ clientnum ].sess.team != ent->client->sess.team )
  1127.             {
  1128.                 continue;
  1129.             }
  1130.         }
  1131.             
  1132.         // this is good, we can use it
  1133.         ent->client->sess.spectatorClient = clientnum;
  1134.         ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
  1135.         return;
  1136.  
  1137.     } while ( clientnum != original );
  1138.  
  1139.     // If being forced to follow and there is a dead client to jump to, then jump to them now
  1140.     if ( deadclient != -1 && g_forceFollow.integer )
  1141.     {
  1142.         // this is good, we can use it
  1143.         ent->client->sess.spectatorClient = deadclient;
  1144.         ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
  1145.         return;
  1146.     }
  1147.  
  1148.        G_StopFollowing( ent );
  1149.  
  1150.     // leave it where it was
  1151. }
  1152.  
  1153. /*
  1154. ==================
  1155. G_SayTo
  1156. ==================
  1157. */
  1158. static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, const char *name, const char *message ) 
  1159. {
  1160.     qboolean     ghost = qfalse;
  1161.     qboolean     spec  = qfalse;
  1162.     const char*  type;
  1163.  
  1164.     if (!other) 
  1165.     {
  1166.         return;
  1167.     }
  1168.  
  1169.     if (!other->inuse) 
  1170.     {
  1171.         return;
  1172.     }
  1173.  
  1174.     if (!other->client) 
  1175.     {
  1176.         return;
  1177.     }
  1178.  
  1179.     if ( other->client->pers.connected != CON_CONNECTED ) 
  1180.     {
  1181.         return;
  1182.     }
  1183.  
  1184.     if ( mode == SAY_TEAM  && !OnSameTeam(ent, other) ) 
  1185.     {
  1186.         return;
  1187.     }
  1188.  
  1189.     if ( !level.intermissiontime && !level.intermissionQueued )
  1190.     {
  1191.         // Spectators cant talk to alive people
  1192.         if ( ent->client->sess.team == TEAM_SPECTATOR )
  1193.         {
  1194.             spec = qtrue;
  1195.         }
  1196.  
  1197.         if ( level.gametypeData->respawnType == RT_NONE )
  1198.         {
  1199.             // Dead people cant talk to alive people
  1200.             if ( !spec && G_IsClientDead ( ent->client ) )
  1201.             {
  1202.                 ghost = qtrue;
  1203.             }
  1204.  
  1205.             // If the client we are talking to is alive then a check
  1206.             // must be made to see if this talker is alowed to speak to this person
  1207.             if ( ent->s.number != other->s.number && !G_IsClientDead ( other->client ) && !G_IsClientSpectating( other->client) && (ghost || spec))
  1208.             {
  1209.                 return;
  1210.             }
  1211.         }
  1212.     }
  1213.  
  1214.     type = "";
  1215.     if ( ghost )
  1216.     {
  1217.         type = "*ghost* ";
  1218.     }
  1219.     else if ( spec )
  1220.     {
  1221.         type = "*spec* ";
  1222.     }
  1223.  
  1224.     trap_SendServerCommand( other-g_entities, va("%s %d \"%s%s%s\"", 
  1225.                             mode == SAY_TEAM ? "tchat" : "chat",
  1226.                             ent->s.number,
  1227.                             type, name, message));
  1228. }
  1229.  
  1230. /*
  1231. ==================
  1232. G_GetChatPrefix
  1233. ==================
  1234. */
  1235. void G_GetChatPrefix ( gentity_t* ent, gentity_t* target, int mode, char* name, int nameSize )
  1236. {
  1237.     const char* namecolor;
  1238.     char        location[64];
  1239.     qboolean    locationOk = qtrue;
  1240.  
  1241.     // Spectators and ghosts dont show locations
  1242.     if ( ent->client->ps.pm_type == PM_DEAD || G_IsClientSpectating ( ent->client ) )
  1243.     {
  1244.         locationOk = qfalse;
  1245.     }
  1246.  
  1247.     if ( !level.gametypeData->teams && mode == SAY_TEAM ) 
  1248.     {
  1249.         mode = SAY_ALL;
  1250.     }
  1251.  
  1252.     if ( level.gametypeData->teams )
  1253.     {
  1254.         switch ( ent->client->sess.team )
  1255.         {
  1256.             case TEAM_BLUE:
  1257.                 namecolor = S_COLOR_BLUE;
  1258.                 break;
  1259.  
  1260.             case TEAM_RED:
  1261.                 namecolor = S_COLOR_RED;
  1262.                 break;
  1263.  
  1264.             default:
  1265.                 namecolor = S_COLOR_WHITE;
  1266.                 break;
  1267.         }
  1268.     }
  1269.     else
  1270.     {
  1271.         namecolor = S_COLOR_WHITE;
  1272.     }
  1273.  
  1274.     switch ( mode ) 
  1275.     {
  1276.         default:
  1277.         case SAY_ALL:
  1278.  
  1279.             Com_sprintf (name, nameSize, "%s%s%s: ", namecolor, ent->client->pers.netname, S_COLOR_WHITE );
  1280.  
  1281.             break;
  1282.  
  1283.         case SAY_TEAM:
  1284.  
  1285.             if ( locationOk && Team_GetLocationMsg(ent, location, sizeof(location)))
  1286.             {
  1287.                 Com_sprintf ( name, nameSize, "%s(%s%s) %s(%s): ", 
  1288.                               namecolor, 
  1289.                               ent->client->pers.netname, 
  1290.                               namecolor,
  1291.                               S_COLOR_WHITE, location );
  1292.             }
  1293.             else
  1294.             {
  1295.                 Com_sprintf ( name, nameSize, "%s(%s%s)%s: ", 
  1296.                               namecolor, 
  1297.                               ent->client->pers.netname, 
  1298.                               namecolor,
  1299.                               S_COLOR_WHITE );
  1300.             }
  1301.             break;
  1302.  
  1303.         case SAY_TELL:
  1304.  
  1305.             if ( locationOk && target && level.gametypeData->teams   && 
  1306.                  target->client->sess.team == ent->client->sess.team  &&
  1307.                  Team_GetLocationMsg(ent, location, sizeof(location))    )
  1308.             {
  1309.                 Com_sprintf ( name, nameSize, "%s[%s%s] %s(%s): ", 
  1310.                               namecolor,
  1311.                               ent->client->pers.netname, 
  1312.                               namecolor,
  1313.                               S_COLOR_WHITE, location );
  1314.             }
  1315.             else
  1316.             {
  1317.                 Com_sprintf ( name, nameSize, "%s[%s%s]%s: ", 
  1318.                               namecolor,
  1319.                               ent->client->pers.netname, 
  1320.                               namecolor,
  1321.                               S_COLOR_WHITE );
  1322.             }
  1323.             break;
  1324.     }
  1325.  
  1326.     strcat ( name, S_COLOR_GREEN );
  1327. }
  1328.  
  1329. /*
  1330. ==================
  1331. G_Say
  1332. ==================
  1333. */
  1334. void G_Say ( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) 
  1335. {
  1336.     int            j;
  1337.     gentity_t    *other;
  1338.     char        text[MAX_SAY_TEXT];
  1339.     char        name[256];
  1340.  
  1341.     // Logging stuff
  1342.     switch ( mode )
  1343.     {
  1344.         case SAY_ALL:
  1345.             G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
  1346.             break;
  1347.  
  1348.         case SAY_TEAM:
  1349.             G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
  1350.             break;
  1351.     }
  1352.  
  1353.     // Generate the chat prefix
  1354.     G_GetChatPrefix ( ent, target, mode, name, sizeof(name) );
  1355.  
  1356.     // Save off the chat text
  1357.     Q_strncpyz( text, chatText, sizeof(text) );
  1358.  
  1359.     if ( target ) 
  1360.     {
  1361.         G_SayTo( ent, target, mode, name, text );
  1362.         return;
  1363.     }
  1364.  
  1365.     // echo the text to the console
  1366.     if ( g_dedicated.integer ) 
  1367.     {
  1368.         Com_Printf( "%s%s\n", name, text);
  1369.     }
  1370.  
  1371.     // send it to all the apropriate clients
  1372.     for (j = 0; j < level.numConnectedClients; j++) 
  1373.     {
  1374.         other = &g_entities[level.sortedClients[j]];
  1375.         G_SayTo( ent, other, mode, name, text );
  1376.     }
  1377. }
  1378.  
  1379.  
  1380. /*
  1381. ==================
  1382. Cmd_Say_f
  1383. ==================
  1384. */
  1385. static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
  1386.     char        *p;
  1387.  
  1388.     if ( trap_Argc () < 2 && !arg0 ) {
  1389.         return;
  1390.     }
  1391.  
  1392.     if (arg0)
  1393.     {
  1394.         p = ConcatArgs( 0 );
  1395.     }
  1396.     else
  1397.     {
  1398.         p = ConcatArgs( 1 );
  1399.     }
  1400.  
  1401.     G_Say( ent, NULL, mode, p );
  1402. }
  1403.  
  1404. /*
  1405. ==================
  1406. Cmd_Tell_f
  1407. ==================
  1408. */
  1409. static void Cmd_Tell_f( gentity_t *ent ) {
  1410.     int            targetNum;
  1411.     gentity_t    *target;
  1412.     char        *p;
  1413.     char        arg[MAX_TOKEN_CHARS];
  1414.  
  1415.     if ( trap_Argc () < 2 ) {
  1416.         return;
  1417.     }
  1418.  
  1419.     trap_Argv( 1, arg, sizeof( arg ) );
  1420.     targetNum = atoi( arg );
  1421.     if ( targetNum < 0 || targetNum >= level.maxclients ) {
  1422.         return;
  1423.     }
  1424.  
  1425.     target = &g_entities[targetNum];
  1426.     if ( !target || !target->inuse || !target->client ) {
  1427.         return;
  1428.     }
  1429.  
  1430.     p = ConcatArgs( 2 );
  1431.  
  1432.     G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );
  1433.     G_Say( ent, target, SAY_TELL, p );
  1434.     // don't tell to the player self if it was already directed to this player
  1435.     // also don't send the chat back to a bot
  1436.     if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
  1437.         G_Say( ent, ent, SAY_TELL, p );
  1438.     }
  1439. }
  1440.  
  1441.  
  1442. static void G_VoiceTo ( gentity_t *ent, gentity_t *other, int mode, const char* name, const char *id, qboolean voiceonly ) 
  1443. {
  1444.     // Only team say is supported right now for voice chatting
  1445.     if (mode != SAY_TEAM) 
  1446.     {
  1447.         return;
  1448.     }
  1449.  
  1450.     if (!other || !other->inuse || !other->client) 
  1451.     {
  1452.         return;
  1453.     }
  1454.  
  1455.     if ( !OnSameTeam(ent, other) ) 
  1456.     {
  1457.         return;
  1458.     }
  1459.  
  1460.     trap_SendServerCommand( other-g_entities, va("%s %d %d \"%s\" \"%s\"", "vtchat", voiceonly, ent->s.number, name, id));
  1461. }
  1462.  
  1463. /*
  1464. ==================
  1465. G_CanVoiceGlobal
  1466.  
  1467. Can we globaly speak right now
  1468. ==================
  1469. */
  1470. qboolean G_CanVoiceGlobal ( void )
  1471. {
  1472.     if ( level.gametypeData->teams && level.time - level.globalVoiceTime > 5000 )
  1473.     {
  1474.         return qtrue;
  1475.     }
  1476.  
  1477.     return qfalse;
  1478. }
  1479.  
  1480. /*
  1481. ==================
  1482. G_VoiceGlobal
  1483.  
  1484. says something out loud that everyone in the radius can hear
  1485. ==================
  1486. */
  1487. void G_VoiceGlobal ( gentity_t* ent, const char* id, qboolean force )
  1488. {
  1489.     if ( !ent )
  1490.     {
  1491.         return;
  1492.     }
  1493.  
  1494.     if ( !level.gametypeData->teams )
  1495.     {
  1496.         return;
  1497.     }
  1498.  
  1499.     if ( !force && level.time - level.globalVoiceTime < 5000 )
  1500.     {
  1501.         return;
  1502.     }
  1503.         
  1504.     level.globalVoiceTime = level.time;
  1505.  
  1506.     trap_SendServerCommand( -1, va("vglobal %d \"%s\"", ent->s.number, id));
  1507. }
  1508.  
  1509. /*
  1510. ==================
  1511. G_Voice
  1512. ==================
  1513. */
  1514. void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) 
  1515. {
  1516.     int            j;
  1517.     gentity_t    *other;
  1518.     char        name[MAX_SAY_TEXT];
  1519.  
  1520.     // Spectators and ghosts dont talk
  1521.     if ( ent->client->ps.pm_type == PM_DEAD || G_IsClientSpectating ( ent->client ) )
  1522.     {
  1523.         return;
  1524.     }
  1525.  
  1526.     // Voice flooding protection on?
  1527.     if ( g_voiceFloodCount.integer )
  1528.     {
  1529.         // If this client has been penalized for voice chatting to much then dont allow the voice chat
  1530.         if ( ent->client->voiceFloodPenalty )
  1531.         {
  1532.             if ( ent->client->voiceFloodPenalty > level.time )
  1533.             {
  1534.                 return;
  1535.             }
  1536.  
  1537.             // No longer penalized
  1538.             ent->client->voiceFloodPenalty = 0;
  1539.         }
  1540.  
  1541.         // See if this client flooded with voice chats
  1542.         ent->client->voiceFloodCount++;
  1543.         if ( ent->client->voiceFloodCount >= g_voiceFloodCount.integer )
  1544.         {
  1545.             ent->client->voiceFloodCount = 0;
  1546.             ent->client->voiceFloodTimer = 0;
  1547.             ent->client->voiceFloodPenalty = level.time + g_voiceFloodPenalty.integer * 1000;
  1548.  
  1549.             trap_SendServerCommand( ent-g_entities, va("print \"Voice chat flooded, you will be able use voice chats again in (%d) seconds\n\"", g_voiceFloodPenalty.integer ) );
  1550.  
  1551.             return;
  1552.         }
  1553.     }
  1554.  
  1555.     G_GetChatPrefix ( ent, target, mode, name, sizeof(name) );
  1556.  
  1557.     if ( target ) 
  1558.     {
  1559.         G_VoiceTo( ent, target, mode, name, id, voiceonly );
  1560.         return;
  1561.     }
  1562.  
  1563.     // send it to all the apropriate clients
  1564.     for (j = 0; j < level.maxclients; j++) 
  1565.     {
  1566.         other = &g_entities[j];
  1567.         G_VoiceTo( ent, other, mode, name, id, voiceonly );
  1568.     }
  1569. }
  1570.  
  1571. /*
  1572. ==================
  1573. Cmd_Voice_f
  1574. ==================
  1575. */
  1576. static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) 
  1577. {
  1578.     char        *p;
  1579.  
  1580.     if ( trap_Argc () < 2 && !arg0 ) {
  1581.         return;
  1582.     }
  1583.  
  1584.     if (arg0)
  1585.     {
  1586.         p = ConcatArgs( 0 );
  1587.     }
  1588.     else
  1589.     {
  1590.         p = ConcatArgs( 1 );
  1591.     }
  1592.  
  1593.     G_Voice( ent, NULL, mode, p, voiceonly );
  1594. }
  1595.  
  1596. /*
  1597. ==================
  1598. Cmd_Where_f
  1599. ==================
  1600. */
  1601. void Cmd_Where_f( gentity_t *ent ) 
  1602. {
  1603.     trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
  1604. }
  1605.  
  1606. /*
  1607. ============
  1608. G_VoteDisabled
  1609.  
  1610. determins if the given vote is disabled
  1611. ============
  1612. */
  1613. int G_VoteDisabled ( const char* callvote ) 
  1614. {
  1615.     return trap_Cvar_VariableIntegerValue( va("novote_%s", callvote) );
  1616. }
  1617.  
  1618. /*
  1619. ==================
  1620. Cmd_CallVote_f
  1621. ==================
  1622. */
  1623. void Cmd_CallVote_f( gentity_t *ent ) 
  1624. {
  1625.     int        i;
  1626.     char    arg1[MAX_STRING_TOKENS];
  1627.     char    arg2[MAX_STRING_TOKENS];
  1628.  
  1629.     if ( !g_allowVote.integer ) 
  1630.     {
  1631.         trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
  1632.         return;
  1633.     }
  1634.  
  1635.     if ( level.intermissiontime || level.intermissionQueued )
  1636.     {
  1637.         trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed during intermission.\n\"" );
  1638.         return;
  1639.     }
  1640.  
  1641.     // No voting within the minute of a map change
  1642.     if ( level.time - level.startTime < 1000 * 60 )
  1643.     {
  1644.         trap_SendServerCommand( ent-g_entities, "print \"Cannot vote within the first minute of a map change.\n\"" );
  1645.         return;
  1646.     }
  1647.  
  1648.     if ( level.numConnectedClients > 1 && level.numVotingClients == 1 ) 
  1649.     {
  1650.         trap_SendServerCommand( ent-g_entities, "print \"You need at least 2 clients to call a vote.\n\"" );
  1651.         return;
  1652.     }
  1653.  
  1654.     if ( level.voteTime ) 
  1655.     {
  1656.         trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" );
  1657.         return;
  1658.     }
  1659.  
  1660.     if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) 
  1661.     {
  1662.         trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" );
  1663.         return;
  1664.     }
  1665.     
  1666.     if ( ent->client->sess.team == TEAM_SPECTATOR ) 
  1667.     {
  1668.         trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
  1669.         return;
  1670.     }
  1671.  
  1672.     if ( ent->client->voteDelayTime > level.time )
  1673.     {
  1674.         trap_SendServerCommand( ent-g_entities, va("print \"You are not allowed to vote within %d minute of a failed vote.\n\"", g_failedVoteDelay.integer ) );
  1675.         return;
  1676.     }
  1677.         
  1678.     // Save the voting client id
  1679.     level.voteClient = ent->s.number;
  1680.  
  1681.     // make sure it is a valid command to vote on
  1682.     trap_Argv( 1, arg1, sizeof( arg1 ) );
  1683.     trap_Argv( 2, arg2, sizeof( arg2 ) );
  1684.  
  1685.     if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) 
  1686.     {
  1687.         trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
  1688.         return;
  1689.     }
  1690.  
  1691.     if ( !Q_stricmp( arg1, "map_restart" ) ) {
  1692.     } else if ( !Q_stricmp( arg1, "mapcycle" ) ) {
  1693.     } else if ( !Q_stricmp( arg1, "map" ) ) {
  1694.     } else if ( !Q_stricmp( arg1, "rmgmap" ) ) {
  1695.     } else if ( !Q_stricmp( arg1, "g_gametype" ) ) {
  1696.     } else if ( !Q_stricmp( arg1, "kick" ) ) {
  1697.     } else if ( !Q_stricmp( arg1, "clientkick" ) ) {
  1698.     } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) {
  1699.     } else if ( !Q_stricmp( arg1, "g_friendlyfire" ) ) {
  1700.     } else if ( !Q_stricmp( arg1, "timelimit" ) ) {
  1701.     } else if ( !Q_stricmp( arg1, "timeextension" ) ) {
  1702.     } else if ( !Q_stricmp( arg1, "scorelimit" ) ) {
  1703.     } else 
  1704.     {
  1705.         trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
  1706.         trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map <mapname>, g_gametype <n>, kick <player>, clientkick <clientnum>, g_doWarmup, timelimit <time>, scorelimit <score>.\n\"" );
  1707.         return;
  1708.     }
  1709.  
  1710.     // see if this particular vote is disabled
  1711.     if ( G_VoteDisabled ( arg1 ) )
  1712.     {
  1713.         trap_SendServerCommand( ent-g_entities, va("print \"The '%s' vote has been disabled on this server.\n\"", arg1) );
  1714.         return;
  1715.     }    
  1716.  
  1717.     // if there is still a vote to be executed
  1718.     if ( level.voteExecuteTime ) 
  1719.     {
  1720.         level.voteExecuteTime = 0;
  1721.         trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
  1722.     }
  1723.  
  1724.     // special case for g_gametype, check for bad values
  1725.     if ( !Q_stricmp( arg1, "g_gametype" ) ) 
  1726.     {
  1727.         // Verify the gametype
  1728.         i = BG_FindGametype ( arg2 );
  1729.         if ( i < 0 )
  1730.         {
  1731.             trap_SendServerCommand( ent-g_entities, "print \"Invalid gametype.\n\"" );
  1732.             return;
  1733.         }    
  1734.  
  1735.         Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
  1736.         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, bg_gametypeData[i].name );
  1737.     } 
  1738.     else if ( !Q_stricmp( arg1, "map" ) ) 
  1739.     {
  1740.         Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
  1741.         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
  1742.     } 
  1743.     else if ( !Q_stricmp( arg1, "rmgmap" ) ) 
  1744.     {
  1745.         char    arg3[MAX_STRING_TOKENS];
  1746.         char    arg4[MAX_STRING_TOKENS];
  1747.  
  1748.         trap_Argv( 3, arg3, sizeof( arg3 ) );
  1749.         trap_Argv( 4, arg4, sizeof( arg4 ) );
  1750.  
  1751.         Com_sprintf( level.voteString, sizeof( level.voteString ), "rmgmap 1 %s 2 %s 3 %s 4 \"%s\" 0", arg2, arg3, arg4, ConcatArgs ( 5 ) );
  1752.         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
  1753.     }
  1754.     else if ( !Q_stricmp( arg1, "mapcycle" ) ) 
  1755.     {
  1756.         if (!*g_mapcycle.string || !Q_stricmp ( g_mapcycle.string, "none" ) ) 
  1757.         {
  1758.             trap_SendServerCommand( ent-g_entities, "print \"there is no map cycle currently set up.\n\"" );
  1759.             return;
  1760.         }
  1761.  
  1762.         Com_sprintf( level.voteString, sizeof( level.voteString ), "mapcycle");
  1763.         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "nextmap" );
  1764.     } 
  1765.     else if ( !Q_stricmp ( arg1, "clientkick" ) )
  1766.     {
  1767.         int n = atoi ( arg2 );
  1768.  
  1769.         if ( n < 0 || n >= MAX_CLIENTS )
  1770.         {
  1771.             trap_SendServerCommand( ent-g_entities, va("print \"invalid client number %d.\n\"", n ) );
  1772.             return;
  1773.         }
  1774.  
  1775.         if ( g_entities[n].client->pers.connected == CON_DISCONNECTED )
  1776.         {
  1777.             trap_SendServerCommand( ent-g_entities, va("print \"there is no client with the client number %d.\n\"", n ) );
  1778.             return;
  1779.         }
  1780.             
  1781.         if ( g_voteKickBanTime.integer )
  1782.         {
  1783.             Com_sprintf ( level.voteString, sizeof(level.voteString ), "banclient %s %d voted off server", arg2, g_voteKickBanTime.integer );
  1784.         }
  1785.         else
  1786.         {
  1787.             Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %s", arg2 );
  1788.         }
  1789.  
  1790.         Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", g_entities[n].client->pers.netname );
  1791.     }
  1792.     else if ( !Q_stricmp ( arg1, "kick" ) )
  1793.     {
  1794.         int clientid = G_ClientNumberFromName ( arg2 );
  1795.  
  1796.         if ( clientid == -1 )
  1797.         {
  1798.             trap_SendServerCommand( ent-g_entities, va("print \"there is no client named '%s' currently on the server.\n\"", arg2 ) );
  1799.             return;
  1800.         }
  1801.  
  1802.         if ( g_voteKickBanTime.integer )
  1803.         {
  1804.             Com_sprintf ( level.voteString, sizeof(level.voteString ), "banclient %d %d voted off server", clientid, g_voteKickBanTime.integer );
  1805.         }
  1806.         else
  1807.         {
  1808.             Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %d", clientid );
  1809.         }
  1810.  
  1811.         Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", g_entities[clientid].client->pers.netname );
  1812.     }
  1813.     else if ( !Q_stricmp ( arg1, "timeextension" ) )
  1814.     {
  1815.         if ( !g_timelimit.integer )
  1816.         {
  1817.             trap_SendServerCommand( ent-g_entities, va("print \"There is no timelimit to extend.\n\"") );
  1818.             return;
  1819.         }
  1820.  
  1821.         if ( !g_timeextension.integer )
  1822.         {
  1823.             trap_SendServerCommand( ent-g_entities, va("print \"This server does not allow time extensions.\n\"") );
  1824.             return;
  1825.         }
  1826.         Com_sprintf ( level.voteString, sizeof(level.voteString ), "extendtime %d", g_timeextension.integer );
  1827.         Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "extend timelimit by %d minutes", g_timeextension.integer );
  1828.     }
  1829.     else 
  1830.     {
  1831.         Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 );
  1832.         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
  1833.     }
  1834.  
  1835.     trap_SendServerCommand( -1, va("print \"%s called a vote.\n\"", ent->client->pers.netname ) );
  1836.  
  1837.     // start the voting, the caller autoamtically votes yes
  1838.     level.voteTime = level.time;
  1839.     level.voteYes = 1;
  1840.     level.voteNo = 0;
  1841.  
  1842.     for ( i = 0 ; i < level.maxclients ; i++ ) 
  1843.     {
  1844.         level.clients[i].ps.eFlags &= ~EF_VOTED;
  1845.     }
  1846.     ent->client->ps.eFlags |= EF_VOTED;
  1847.  
  1848.     trap_SetConfigstring( CS_VOTE_TIME, va("%i,%i", level.voteTime, g_voteDuration.integer*1000 ) );
  1849.     trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString );    
  1850.     trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
  1851.     trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );    
  1852.     trap_SetConfigstring( CS_VOTE_NEEDED, va("%i", level.numVotingClients / 2 ) );
  1853. }
  1854.  
  1855. /*
  1856. ==================
  1857. Cmd_Vote_f
  1858. ==================
  1859. */
  1860. void Cmd_Vote_f( gentity_t *ent ) 
  1861. {
  1862.     char msg[64];
  1863.  
  1864.     if ( !level.voteTime ) 
  1865.     {
  1866.         trap_SendServerCommand( ent-g_entities, "print \"No vote in progress.\n\"" );
  1867.         return;
  1868.     }
  1869.  
  1870.     if ( ent->client->ps.eFlags & EF_VOTED ) 
  1871.     {
  1872.         trap_SendServerCommand( ent-g_entities, "print \"Vote already cast.\n\"" );
  1873.         return;
  1874.     }
  1875.  
  1876.     if ( ent->client->sess.team == TEAM_SPECTATOR ) 
  1877.     {
  1878.         trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator.\n\"" );
  1879.         return;
  1880.     }
  1881.  
  1882.     trap_SendServerCommand( ent-g_entities, "print \"Vote cast.\n\"" );
  1883.  
  1884.     ent->client->ps.eFlags |= EF_VOTED;
  1885.  
  1886.     trap_Argv( 1, msg, sizeof( msg ) );
  1887.  
  1888.     if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) 
  1889.     {
  1890.         level.voteYes++;
  1891.         trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
  1892.     } 
  1893.     else 
  1894.     {
  1895.         level.voteNo++;
  1896.         trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );    
  1897.     }
  1898.  
  1899.     // a majority will be determined in CheckVote, which will also account
  1900.     // for players entering or leaving
  1901. }
  1902.  
  1903.  
  1904. /*
  1905. =================
  1906. Cmd_SetViewpos_f
  1907. =================
  1908. */
  1909. void Cmd_SetViewpos_f( gentity_t *ent ) 
  1910. {
  1911.     vec3_t        origin, angles;
  1912.     char        buffer[MAX_TOKEN_CHARS];
  1913.     int            i;
  1914.  
  1915.     if ( !g_cheats.integer ) 
  1916.     {
  1917.         trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
  1918.         return;
  1919.     }
  1920.     
  1921.     if ( trap_Argc() != 5 ) 
  1922.     {
  1923.         trap_SendServerCommand( ent-g_entities, va("print \"usage: setviewpos x y z yaw\n\""));
  1924.         return;
  1925.     }
  1926.  
  1927.     VectorClear( angles );
  1928.     for ( i = 0 ; i < 3 ; i++ ) 
  1929.     {
  1930.         trap_Argv( i + 1, buffer, sizeof( buffer ) );
  1931.         origin[i] = atof( buffer );
  1932.     }
  1933.  
  1934.     trap_Argv( 4, buffer, sizeof( buffer ) );
  1935.     angles[YAW] = atof( buffer );
  1936.  
  1937.     TeleportPlayer( ent, origin, angles );
  1938. }
  1939.  
  1940. /*
  1941. =================
  1942. ClientCommand
  1943. =================
  1944. */
  1945. void ClientCommand( int clientNum ) {
  1946.     gentity_t *ent;
  1947.     char    cmd[MAX_TOKEN_CHARS];
  1948.  
  1949.     ent = g_entities + clientNum;
  1950.     if ( !ent->client ) {
  1951.         return;        // not fully in game yet
  1952.     }
  1953.  
  1954.  
  1955.     trap_Argv( 0, cmd, sizeof( cmd ) );
  1956.  
  1957.     //rww - redirect bot commands
  1958.     if (strstr(cmd, "bot_") && AcceptBotCommand(cmd, ent))
  1959.     {
  1960.         return;
  1961.     }
  1962.     //end rww
  1963.  
  1964.     if (Q_stricmp (cmd, "say") == 0) {
  1965.         Cmd_Say_f (ent, SAY_ALL, qfalse);
  1966.         return;
  1967.     }
  1968.     if (Q_stricmp (cmd, "say_team") == 0) {
  1969.         Cmd_Say_f (ent, SAY_TEAM, qfalse);
  1970.         return;
  1971.     }
  1972.     
  1973.     if (Q_stricmp (cmd, "tell") == 0) 
  1974.     {
  1975.         Cmd_Tell_f ( ent );
  1976.         return;
  1977.     }
  1978.  
  1979.     if (Q_stricmp (cmd, "vsay_team") == 0) 
  1980.     {
  1981.         Cmd_Voice_f (ent, SAY_TEAM, qfalse, qfalse);
  1982.         return;
  1983.     }
  1984.  
  1985.     if (Q_stricmp (cmd, "score") == 0) {
  1986.         Cmd_Score_f (ent);
  1987.         return;
  1988.     }
  1989.  
  1990.     if (Q_stricmp (cmd, "team") == 0)
  1991.     {
  1992.         Cmd_Team_f (ent);
  1993.         return;
  1994.     }
  1995.  
  1996.     // ignore all other commands when at intermission
  1997.     if (level.intermissiontime) 
  1998.     {
  1999. //        Cmd_Say_f (ent, qfalse, qtrue);
  2000.         return;
  2001.     }
  2002.  
  2003.     if ( Q_stricmp ( cmd, "drop" ) == 0 )
  2004.         Cmd_Drop_f ( ent );
  2005.     else if (Q_stricmp (cmd, "dropitem" ) == 0 )
  2006.         Cmd_DropItem_f ( ent );
  2007.     else if (Q_stricmp (cmd, "give") == 0)
  2008.         Cmd_Give_f (ent);
  2009.     else if (Q_stricmp (cmd, "god") == 0)
  2010.         Cmd_God_f (ent);
  2011.     else if (Q_stricmp (cmd, "notarget") == 0)
  2012.         Cmd_Notarget_f (ent);
  2013.     else if (Q_stricmp (cmd, "noclip") == 0)
  2014.         Cmd_Noclip_f (ent);
  2015.     else if (Q_stricmp (cmd, "kill") == 0)
  2016.         Cmd_Kill_f (ent);
  2017.     else if (Q_stricmp (cmd, "levelshot") == 0)
  2018.         Cmd_LevelShot_f (ent);
  2019.     else if (Q_stricmp (cmd, "follow") == 0)
  2020.         Cmd_Follow_f (ent);
  2021.     else if (Q_stricmp (cmd, "follownext") == 0)
  2022.         Cmd_FollowCycle_f (ent, 1);
  2023.     else if (Q_stricmp (cmd, "followprev") == 0)
  2024.         Cmd_FollowCycle_f (ent, -1);
  2025.     else if (Q_stricmp (cmd, "where") == 0)
  2026.         Cmd_Where_f (ent);
  2027.     else if (Q_stricmp (cmd, "callvote") == 0)
  2028.         Cmd_CallVote_f (ent);
  2029.     else if (Q_stricmp (cmd, "vote") == 0)
  2030.         Cmd_Vote_f (ent);
  2031.     else if (Q_stricmp (cmd, "setviewpos") == 0)
  2032.         Cmd_SetViewpos_f( ent );
  2033.  
  2034. #ifdef _SOF2_BOTS
  2035.     else if (Q_stricmp (cmd, "addbot") == 0)
  2036.         trap_SendServerCommand( clientNum, va("print \"ADDBOT command can only be used via RCON\n\"" ) );
  2037. #endif
  2038.  
  2039.     else
  2040.         trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
  2041. }
  2042.